package net.ikenway.springboot.demo.Config;

import com.google.gson.Gson;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import net.ikenway.springboot.demo.Model.Dto.ResponseBodyDto;
import net.ikenway.springboot.demo.Utils.JwtTokenUtil;
import net.ikenway.springboot.demo.Utils.SpringContextUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.security.auth.message.AuthException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


/**
 * @author MasterKenway <zk@ikenway.net>
 * @Description
 * @date Created Date 12/14/2020
 */
@Configuration
public class JwtAuthConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<JwtAuthConfigurer<H>, H> {


    @Override
    public void configure(H builder) throws Exception {
        builder.addFilterBefore(new JwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    static class JwtTokenFilter extends OncePerRequestFilter {
        private static final String AUTH_PREFIX_URI = "/api/auth";

        private final UserDetailsService userDetailsService;

        private final JwtTokenUtil jwtTokenUtil;

        // Filter 的运行等级高于Bean 无法直接使用 @AutoWired 进行注入
        // 因此通过工具类来获取上下文 从而来获取Bean
        public JwtTokenFilter() {
            this.userDetailsService = SpringContextUtil.getContext().getBean(UserDetailsService.class);
            this.jwtTokenUtil = SpringContextUtil.getContext().getBean(JwtTokenUtil.class);
        }

        @Override
        protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
            if (httpServletRequest.getRequestURI().contains("/api/image/") ||
                    httpServletRequest.getRequestURI().contains("/api/excels/")) {
                filterChain.doFilter(httpServletRequest, httpServletResponse);
                return;
            }

            //
            if (httpServletRequest.getRequestURI().contains(AUTH_PREFIX_URI)) {
                filterChain.doFilter(httpServletRequest, httpServletResponse);
                return;
            }

            httpServletResponse.setContentType("application/json;charset=utf-8");
            Gson gson = new Gson();

            try {
                String token = httpServletRequest.getHeader("Authorization");
                if (token == null) {
//                httpServletResponse.getWriter().print(gson.toJson(new ResponseBodyDto(400, "用户未登录")));
                    throw new AuthException("用户未登录");
                }
//                Matcher matcher = Pattern.compile("Bearer (.*?)$", Pattern.CASE_INSENSITIVE).matcher(httpServletRequest.getHeader("Authorization"));
//                String token = null;
//                while(matcher.find()) {
//                    token = matcher.group(0);
//                }


                // 验证Token
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(jwtTokenUtil.getUsernameFromToken(token));
                if (!jwtTokenUtil.validateToken(token, userDetails)) {
//                httpServletResponse.getWriter().print(gson.toJson(new ResponseBodyDto(400, "用户未登录")));
                    throw new AuthException("用户未登录");
                }

                // 通过验证
                SecurityContext context = SecurityContextHolder.getContext();
                if (context.getAuthentication() == null) {
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authenticationToken.setDetails(userDetails);
                    context.setAuthentication(authenticationToken);
                }
            } catch (AuthException exception) {
                httpServletResponse.getWriter().print(gson.toJson(new ResponseBodyDto(400, "用户未登录")));
                return;
            } catch (ExpiredJwtException expiredJwtException) {
                httpServletResponse.getWriter().print(gson.toJson(new ResponseBodyDto(400, "token已过期")));
                return;
            } catch (JwtException jwtException) {
                httpServletResponse.getWriter().print(gson.toJson(new ResponseBodyDto(400, "token无效")));
                return;
            }

            filterChain.doFilter(httpServletRequest, httpServletResponse);
        }
    }


}
